use salvo::prelude::*;
use salvo::http::{ResBody, StatusCode};
use salvo::hyper;
use serde_json;
use futures::StreamExt;
use crate::utils::ApiResponse;

#[derive(Clone)]
pub struct ResponseConverter;

#[async_trait]
impl Handler for ResponseConverter {
    async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
        ctrl.call_next(req, depot, res).await;

        let status = res.status_code.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
        let body = res.take_body();
        let api_response = match body {
            ResBody::Once(bytes) => {
                if status.is_success() {
                    match serde_json::from_slice::<serde_json::Value>(&bytes) {
                        Ok(json) => ApiResponse::new(status.as_u16() as i32, "success", Some(json)),
                        Err(_) => ApiResponse::error(500, "Invalid JSON response")
                    }
                } else {
                    let message = String::from_utf8_lossy(&bytes).into_owned();
                    ApiResponse::error(status.as_u16() as i32, message)
                }
            }
            ResBody::Chunks(chunks) => {
                let bytes: Vec<u8> = chunks.into_iter().flat_map(|c| c.into_iter()).collect();
                if status.is_success() {
                    match serde_json::from_slice::<serde_json::Value>(&bytes) {
                        Ok(json) => ApiResponse::new(status.as_u16() as i32, "success", Some(json)),
                        Err(_) => ApiResponse::error(500, "Invalid JSON response")
                    }
                } else {
                    let message = String::from_utf8_lossy(&bytes).into_owned();
                    ApiResponse::error(status.as_u16() as i32, message)
                }
            }
            ResBody::None => {
                if status.is_success() {
                    ApiResponse::new(status.as_u16() as i32, "Empty response processed successfully", None)
                } else {
                    let reason = status.canonical_reason().unwrap_or("Unknown error");
                    ApiResponse::error(status.as_u16() as i32, &format!("{} (Empty response body)", reason))
                }
            }
            ResBody::Boxed(_) => {
                ApiResponse::error(501, &format!("Boxed response type not implemented (Status: {})", status))
            }
            ResBody::Stream(_) => {
                ApiResponse::error(501, &format!("Stream response type not implemented (Status: {})", status))
            }
            ResBody::Hyper(_) => {
                ApiResponse::error(501, &format!("Hyper body type not implemented (Status: {})", status))
            },
            ResBody::Channel(_) => {
                ApiResponse::error(501, &format!("Channel response type not implemented (Status: {})", status))
            },
            ResBody::Error(err) => {
                ApiResponse::error(err.code.as_u16() as i32, &err.to_string())
            },
            _ => ApiResponse::error(500, "Unsupported response body type")
        };

        res.render(Json(api_response));
    }
}

pub fn response_converter() -> ResponseConverter {
    ResponseConverter
}
